59. 自定义 Prometheus Operator 监控项

上节课和大家讲解了 Prometheus Operator 的安装和基本使用方法,这节课给大家介绍如何在 Prometheus Operator 中添加一个自定义的监控项。

除了 Kubernetes 集群中的一些资源对象、节点以及组件需要监控,有的时候我们可能还需要根据实际的业务需求去添加自定义的监控项,添加一个自定义监控的步骤也是非常简单的。

  • 第一步建立一个 ServiceMonitor 对象,用于 Prometheus 添加监控项
  • 第二步为 ServiceMonitor 对象关联 metrics 数据接口的一个 Service 对象
  • 第三步确保 Service 对象可以正确获取到 metrics 数据

接下来我们就来为大家演示如何添加 etcd 集群的监控。

无论是 Kubernetes 集群外的还是使用 Kubeadm 安装在集群内部的 etcd 集群,我们这里都将其视作集群外的独立集群,因为对于二者的使用方法没什么特殊之处。

etcd 证书

对于 etcd 集群一般情况下,为了安全都会开启 https 证书认证的方式,所以要想让 Prometheus 访问到 etcd 集群的监控数据,就需要提供相应的证书校验。

由于我们这里演示环境使用的是 Kubeadm 搭建的集群,我们可以使用 kubectl 工具去获取 etcd 启动的时候使用的证书路径:

  1. $ kubectl get pods -n kube-system
  2. NAME READY STATUS RESTARTS AGE
  3. etcd-master 1/1 Running 0 2h
  4. $ kubectl get pod etcd-master -n kube-system -o yaml
  5. ......
  6. spec:
  7. containers:
  8. - command:
  9. - etcd
  10. - --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt
  11. - --listen-client-urls=https://127.0.0.1:2379
  12. - --advertise-client-urls=https://127.0.0.1:2379
  13. - --client-cert-auth=true
  14. - --peer-client-cert-auth=true
  15. - --data-dir=/var/lib/etcd
  16. - --cert-file=/etc/kubernetes/pki/etcd/server.crt
  17. - --key-file=/etc/kubernetes/pki/etcd/server.key
  18. - --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
  19. - --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
  20. - --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
  21. image: k8s.gcr.io/etcd-amd64:3.1.12
  22. imagePullPolicy: IfNotPresent
  23. livenessProbe:
  24. exec:
  25. command:
  26. - /bin/sh
  27. - -ec
  28. - ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt
  29. --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key
  30. get foo
  31. failureThreshold: 8
  32. initialDelaySeconds: 15
  33. periodSeconds: 10
  34. successThreshold: 1
  35. timeoutSeconds: 15
  36. name: etcd
  37. resources: {}
  38. terminationMessagePath: /dev/termination-log
  39. terminationMessagePolicy: File
  40. volumeMounts:
  41. - mountPath: /var/lib/etcd
  42. name: etcd-data
  43. - mountPath: /etc/kubernetes/pki/etcd
  44. name: etcd-certs
  45. ......
  46. tolerations:
  47. - effect: NoExecute
  48. operator: Exists
  49. volumes:
  50. - hostPath:
  51. path: /var/lib/etcd
  52. type: DirectoryOrCreate
  53. name: etcd-data
  54. - hostPath:
  55. path: /etc/kubernetes/pki/etcd
  56. type: DirectoryOrCreate
  57. name: etcd-certs
  58. ......

我们可以看到 etcd 使用的证书都对应在节点的 /etc/kubernetes/pki/etcd 这个路径下面,所以首先我们将需要使用到的证书通过 secret 对象保存到集群中去:(在 etcd 运行的节点)

  1. $ kubectl -n monitoring create secret generic etcd-certs --from-file=/etc/kubernetes/pki/etcd/healthcheck-client.crt --from-file=/etc/kubernetes/pki/etcd/healthcheck-client.key --from-file=/etc/kubernetes/pki/etcd/ca.crt
  2. secret "etcd-certs" created

如果你是独立的二进制方式启动的 etcd 集群,同样将对应的证书保存到集群中的一个 secret 对象中去即可。

然后将上面创建的 etcd-certs 对象配置到 prometheus 资源对象中,直接更新 prometheus 资源对象即可:

  1. $ kubectl edit prometheus k8s -n monitoring

添加如下的 secrets 属性:

  1. nodeSelector:
  2. beta.kubernetes.io/os: linux
  3. replicas: 2
  4. secrets:
  5. - etcd-certs

更新完成后,我们就可以在 Prometheus 的 Pod 中获取到上面创建的 etcd 证书文件了,具体的路径我们可以进入 Pod 中查看:

  1. $ kubectl exec -it prometheus-k8s-0 /bin/sh -n monitoring
  2. Defaulting container name to prometheus.
  3. Use 'kubectl describe pod/prometheus-k8s-0 -n monitoring' to see all of the containers in this pod.
  4. / $ ls /etc/prometheus/secrets/etcd-certs/
  5. ca.crt healthcheck-client.crt healthcheck-client.key

创建 ServiceMonitor

现在 Prometheus 访问 etcd 集群的证书已经准备好了,接下来创建 ServiceMonitor 对象即可(prometheus-serviceMonitorEtcd.yaml)

  1. apiVersion: monitoring.coreos.com/v1
  2. kind: ServiceMonitor
  3. metadata:
  4. name: etcd-k8s
  5. namespace: monitoring
  6. labels:
  7. k8s-app: etcd-k8s
  8. spec:
  9. jobLabel: k8s-app
  10. endpoints:
  11. - port: port
  12. interval: 30s
  13. scheme: https
  14. tlsConfig:
  15. caFile: /etc/prometheus/secrets/etcd-certs/ca.crt
  16. certFile: /etc/prometheus/secrets/etcd-certs/healthcheck-client.crt
  17. keyFile: /etc/prometheus/secrets/etcd-certs/healthcheck-client.key
  18. insecureSkipVerify: true
  19. selector:
  20. matchLabels:
  21. k8s-app: etcd
  22. namespaceSelector:
  23. matchNames:
  24. - kube-system

上面我们在 monitoring 命名空间下面创建了名为 etcd-k8s 的 ServiceMonitor 对象,基本属性和前面章节中的一致,匹配 kube-system 这个命名空间下面的具有 k8s-app=etcd 这个 label 标签的 Service,jobLabel 表示用于检索 job 任务名称的标签,和前面不太一样的地方是 endpoints 属性的写法,配置上访问 etcd 的相关证书,endpoints 属性下面可以配置很多抓取的参数,比如 relabel、proxyUrl,tlsConfig 表示用于配置抓取监控数据端点的 tls 认证,由于证书 serverName 和 etcd 中签发的可能不匹配,所以加上了 insecureSkipVerify=true

tlsConfig

关于 ServiceMonitor 属性的更多用法可以查看文档:https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md 了解更多

直接创建这个 ServiceMonitor 对象:

  1. $ kubectl create -f prometheus-serviceMonitorEtcd.yaml
  2. servicemonitor.monitoring.coreos.com "etcd-k8s" created

创建 Service

ServiceMonitor 创建完成了,但是现在还没有关联的对应的 Service 对象,所以需要我们去手动创建一个 Service 对象(prometheus-etcdService.yaml):

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: etcd-k8s
  5. namespace: kube-system
  6. labels:
  7. k8s-app: etcd
  8. spec:
  9. type: ClusterIP
  10. clusterIP: None
  11. ports:
  12. - name: port
  13. port: 2379
  14. protocol: TCP
  15. ---
  16. apiVersion: v1
  17. kind: Endpoints
  18. metadata:
  19. name: etcd-k8s
  20. namespace: kube-system
  21. labels:
  22. k8s-app: etcd
  23. subsets:
  24. - addresses:
  25. - ip: 10.151.30.57
  26. nodeName: etc-master
  27. ports:
  28. - name: port
  29. port: 2379
  30. protocol: TCP

我们这里创建的 Service 没有采用前面通过 label 标签的形式去匹配 Pod 的做法,因为前面我们说过很多时候我们创建的 etcd 集群是独立于集群之外的,这种情况下面我们就需要自定义一个 Endpoints,要注意 metadata 区域的内容要和 Service 保持一致,Service 的 clusterIP 设置为 None,对改知识点不太熟悉的,可以去查看我们前面关于 Service 部分的讲解。

Endpoints 的 subsets 中填写 etcd 集群的地址即可,我们这里是单节点的,填写一个即可,直接创建该 Service 资源:

  1. $ kubectl create -f prometheus-etcdService.yaml

创建完成后,隔一会儿去 Prometheus 的 Dashboard 中查看 targets,便会有 etcd 的监控项了:

prometheus etcd

可以看到还是有一个明显的错误,和我们上节课监控 kube-scheduler 的错误比较类似于,因为我们这里的 etcd 的是监听在 127.0.0.1 这个 IP 上面的,所以访问会拒绝:

  1. --listen-client-urls=https://127.0.0.1:2379

同样我们只需要在 /etc/kubernetes/manifest/ 目录下面(static pod 默认的目录)的 etcd.yaml 文件中将上面的listen-client-urls更改成 0.0.0.0 即可:

  1. --listen-client-urls=https://0.0.0.0:2379

重启 etcd,生效后,查看 etcd 这个监控任务就正常了:

prometheus etcd

数据采集到后,可以在 grafana 中导入编号为3070的 dashboard,获取到 etcd 的监控图表。

grafana etcd dashboard

配置 PrometheusRule

现在我们知道怎么自定义一个 ServiceMonitor 对象了,但是如果需要自定义一个报警规则的话呢?比如现在我们去查看 Prometheus Dashboard 的 Alert 页面下面就已经有一些报警规则了,还有一些是已经触发规则的了:

alerts

但是这些报警信息是哪里来的呢?他们应该用怎样的方式通知我们呢?我们知道之前我们使用自定义的方式可以在 Prometheus 的配置文件之中指定 AlertManager 实例和 报警的 rules 文件,现在我们通过 Operator 部署的呢?我们可以在 Prometheus Dashboard 的 Config 页面下面查看关于 AlertManager 的配置:

  1. alerting:
  2. alert_relabel_configs:
  3. - separator: ;
  4. regex: prometheus_replica
  5. replacement: $1
  6. action: labeldrop
  7. alertmanagers:
  8. - kubernetes_sd_configs:
  9. - role: endpoints
  10. namespaces:
  11. names:
  12. - monitoring
  13. scheme: http
  14. path_prefix: /
  15. timeout: 10s
  16. relabel_configs:
  17. - source_labels: [__meta_kubernetes_service_name]
  18. separator: ;
  19. regex: alertmanager-main
  20. replacement: $1
  21. action: keep
  22. - source_labels: [__meta_kubernetes_endpoint_port_name]
  23. separator: ;
  24. regex: web
  25. replacement: $1
  26. action: keep
  27. rule_files:
  28. - /etc/prometheus/rules/prometheus-k8s-rulefiles-0/*.yaml

上面 alertmanagers 实例的配置我们可以看到是通过角色为 endpoints 的 kubernetes 的服务发现机制获取的,匹配的是服务名为 alertmanager-main,端口名未 web 的 Service 服务,我们查看下 alertmanager-main 这个 Service:

  1. kubectl describe svc alertmanager-main -n monitoring
  2. Name: alertmanager-main
  3. Namespace: monitoring
  4. Labels: alertmanager=main
  5. Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"alertmanager":"main"},"name":"alertmanager-main","namespace":"monitoring"},...
  6. Selector: alertmanager=main,app=alertmanager
  7. Type: NodePort
  8. IP: 10.104.156.29
  9. Port: web 9093/TCP
  10. TargetPort: web/TCP
  11. NodePort: web 31918/TCP
  12. Endpoints: 10.244.2.34:9093,10.244.2.37:9093,10.244.4.109:9093
  13. Session Affinity: None
  14. External Traffic Policy: Cluster
  15. Events: <none>

可以看到服务名正是 alertmanager-main,Port 定义的名称也是 web,符合上面的规则,所以 Prometheus 和 AlertManager 组件就正确关联上了。而对应的报警规则文件位于:/etc/prometheus/rules/prometheus-k8s-rulefiles-0/目录下面所有的 YAML 文件。我们可以进入 Prometheus 的 Pod 中验证下该目录下面是否有 YAML 文件:

  1. $ kubectl exec -it prometheus-k8s-0 /bin/sh -n monitoring
  2. Defaulting container name to prometheus.
  3. Use 'kubectl describe pod/prometheus-k8s-0 -n monitoring' to see all of the containers in this pod.
  4. /prometheus $ ls /etc/prometheus/rules/prometheus-k8s-rulefiles-0/
  5. monitoring-prometheus-k8s-rules.yaml
  6. /prometheus $ cat /etc/prometheus/rules/prometheus-k8s-rulefiles-0/monitoring-pr
  7. ometheus-k8s-rules.yaml
  8. groups:
  9. - name: k8s.rules
  10. rules:
  11. - expr: |
  12. sum(rate(container_cpu_usage_seconds_total{job="kubelet", image!="", container_name!=""}[5m])) by (namespace)
  13. record: namespace:container_cpu_usage_seconds_total:sum_rate
  14. ......

这个 YAML 文件实际上就是我们之前创建的一个 PrometheusRule 文件包含的:

  1. $ cat prometheus-rules.yaml
  2. apiVersion: monitoring.coreos.com/v1
  3. kind: PrometheusRule
  4. metadata:
  5. labels:
  6. prometheus: k8s
  7. role: alert-rules
  8. name: prometheus-k8s-rules
  9. namespace: monitoring
  10. spec:
  11. groups:
  12. - name: k8s.rules
  13. rules:
  14. - expr: |
  15. sum(rate(container_cpu_usage_seconds_total{job="kubelet", image!="", container_name!=""}[5m])) by (namespace)
  16. record: namespace:container_cpu_usage_seconds_total:sum_rate

我们这里的 PrometheusRule 的 name 为 prometheus-k8s-rules,namespace 为 monitoring,我们可以猜想到我们创建一个 PrometheusRule 资源对象后,会自动在上面的 prometheus-k8s-rulefiles-0 目录下面生成一个对应的<namespace>-<name>.yaml文件,所以如果以后我们需要自定义一个报警选项的话,只需要定义一个 PrometheusRule 资源对象即可。至于为什么 Prometheus 能够识别这个 PrometheusRule 资源对象呢?这就需要查看我们创建的 prometheus 这个资源对象了,里面有非常重要的一个属性 ruleSelector,用来匹配 rule 规则的过滤器,要求匹配具有 prometheus=k8s 和 role=alert-rules 标签的 PrometheusRule 资源对象,现在明白了吧?

  1. ruleSelector:
  2. matchLabels:
  3. prometheus: k8s
  4. role: alert-rules

所以我们要想自定义一个报警规则,只需要创建一个具有 prometheus=k8s 和 role=alert-rules 标签的 PrometheusRule 对象就行了,比如现在我们添加一个 etcd 是否可用的报警,我们知道 etcd 整个集群有一半以上的节点可用的话集群就是可用的,所以我们判断如果不可用的 etcd 数量超过了一半那么就触发报警,创建文件 prometheus-etcdRules.yaml:

  1. apiVersion: monitoring.coreos.com/v1
  2. kind: PrometheusRule
  3. metadata:
  4. labels:
  5. prometheus: k8s
  6. role: alert-rules
  7. name: etcd-rules
  8. namespace: monitoring
  9. spec:
  10. groups:
  11. - name: etcd
  12. rules:
  13. - alert: EtcdClusterUnavailable
  14. annotations:
  15. summary: etcd cluster small
  16. description: If one more etcd peer goes down the cluster will be unavailable
  17. expr: |
  18. count(up{job="etcd"} == 0) > (count(up{job="etcd"}) / 2 - 1)
  19. for: 3m
  20. labels:
  21. severity: critical

注意 label 标签一定至少要有 prometheus=k8s 和 role=alert-rules,创建完成后,隔一会儿再去容器中查看下 rules 文件夹:

  1. kubectl exec -it prometheus-k8s-0 /bin/sh -n monitoring
  2. Defaulting container name to prometheus.
  3. Use 'kubectl describe pod/prometheus-k8s-0 -n monitoring' to see all of the containers in this pod.
  4. /prometheus $ ls /etc/prometheus/rules/prometheus-k8s-rulefiles-0/
  5. monitoring-etcd-rules.yaml monitoring-prometheus-k8s-rules.yaml

可以看到我们创建的 rule 文件已经被注入到了对应的 rulefiles 文件夹下面了,证明我们上面的设想是正确的。然后再去 Prometheus Dashboard 的 Alert 页面下面就可以查看到上面我们新建的报警规则了:

etcd cluster

配置报警

我们知道了如何去添加一个报警规则配置项,但是这些报警信息用怎样的方式去发送呢?前面的课程中我们知道我们可以通过 AlertManager 的配置文件去配置各种报警接收器,现在我们是通过 Operator 提供的 alertmanager 资源对象创建的组件,应该怎样去修改配置呢?

首先我们将 alertmanager-main 这个 Service 改为 NodePort 类型的 Service,修改完成后我们可以在页面上的 status 路径下面查看 AlertManager 的配置信息:

alertmanager config

这些配置信息实际上是来自于我们之前在prometheus-operator/contrib/kube-prometheus/manifests目录下面创建的 alertmanager-secret.yaml 文件:

  1. apiVersion: v1
  2. data:
  3. alertmanager.yaml: Imdsb2JhbCI6IAogICJyZXNvbHZlX3RpbWVvdXQiOiAiNW0iCiJyZWNlaXZlcnMiOiAKLSAibmFtZSI6ICJudWxsIgoicm91dGUiOiAKICAiZ3JvdXBfYnkiOiAKICAtICJqb2IiCiAgImdyb3VwX2ludGVydmFsIjogIjVtIgogICJncm91cF93YWl0IjogIjMwcyIKICAicmVjZWl2ZXIiOiAibnVsbCIKICAicmVwZWF0X2ludGVydmFsIjogIjEyaCIKICAicm91dGVzIjogCiAgLSAibWF0Y2giOiAKICAgICAgImFsZXJ0bmFtZSI6ICJEZWFkTWFuc1N3aXRjaCIKICAgICJyZWNlaXZlciI6ICJudWxsIg==
  4. kind: Secret
  5. metadata:
  6. name: alertmanager-main
  7. namespace: monitoring
  8. type: Opaque

可以将 alertmanager.yaml 对应的 value 值做一个 base64 解码:

  1. $ echo "Imdsb2JhbCI6IAogICJyZXNvbHZlX3RpbWVvdXQiOiAiNW0iCiJyZWNlaXZlcnMiOiAKLSAibmFtZSI6ICJudWxsIgoicm91dGUiOiAKICAiZ3JvdXBfYnkiOiAKICAtICJqb2IiCiAgImdyb3VwX2ludGVydmFsIjogIjVtIgogICJncm91cF93YWl0IjogIjMwcyIKICAicmVjZWl2ZXIiOiAibnVsbCIKICAicmVwZWF0X2ludGVydmFsIjogIjEyaCIKICAicm91dGVzIjogCiAgLSAibWF0Y2giOiAKICAgICAgImFsZXJ0bmFtZSI6ICJEZWFkTWFuc1N3aXRjaCIKICAgICJyZWNlaXZlciI6ICJudWxsIg==" | base64 -d
  2. "global":
  3. "resolve_timeout": "5m"
  4. "receivers":
  5. - "name": "null"
  6. "route":
  7. "group_by":
  8. - "job"
  9. "group_interval": "5m"
  10. "group_wait": "30s"
  11. "receiver": "null"
  12. "repeat_interval": "12h"
  13. "routes":
  14. - "match":
  15. "alertname": "DeadMansSwitch"
  16. "receiver": "null"

我们可以看到内容和上面查看的配置信息是一致的,所以如果我们想要添加自己的接收器,或者模板消息,我们就可以更改这个文件:

  1. global:
  2. resolve_timeout: 5m
  3. smtp_smarthost: 'smtp.163.com:25'
  4. smtp_from: 'ych_1024@163.com'
  5. smtp_auth_username: 'ych_1024@163.com'
  6. smtp_auth_password: '<邮箱密码>'
  7. smtp_hello: '163.com'
  8. smtp_require_tls: false
  9. route:
  10. group_by: ['job', 'severity']
  11. group_wait: 30s
  12. group_interval: 5m
  13. repeat_interval: 12h
  14. receiver: default
  15. routes:
  16. - receiver: webhook
  17. match:
  18. alertname: CoreDNSDown
  19. receivers:
  20. - name: 'default'
  21. email_configs:
  22. - to: '517554016@qq.com'
  23. send_resolved: true
  24. - name: 'webhook'
  25. webhook_configs:
  26. - url: 'http://dingtalk-hook.kube-ops:5000'
  27. send_resolved: true

将上面文件保存为 alertmanager.yaml,然后使用这个文件创建一个 Secret 对象:

  1. # 先将之前的 secret 对象删除
  2. $ kubectl delete secret alertmanager-main -n monitoring
  3. secret "alertmanager-main" deleted
  4. $ kubectl create secret generic alertmanager-main --from-file=alertmanager.yaml -n monitoring
  5. secret "alertmanager-main" created

我们添加了两个接收器,默认的通过邮箱进行发送,对于 CoreDNSDown 这个报警我们通过 webhook 来进行发送,这个 webhook 就是我们前面课程中定义的一个钉钉接收的 Server,上面的步骤创建完成后,很快我们就会收到一条钉钉消息:

钉钉

同样邮箱中也会收到报警信息:

邮箱

我们再次查看 AlertManager 页面的 status 页面的配置信息可以看到已经变成上面我们的配置信息了:

alertmanager config

AlertManager 配置也可以使用模板(.tmpl文件),这些模板可以与 alertmanager.yaml 配置文件一起添加到 Secret 对象中,比如:

  1. apiVersionv1
  2. kindsecret
  3. metadata
  4. namealertmanager-example
  5. data
  6. alertmanager.yaml:{BASE64_CONFIG}
  7. template_1.tmpl:{BASE64_TEMPLATE_1}
  8. template_2.tmpl:{BASE64_TEMPLATE_2}
  9. ...

模板会被放置到与配置文件相同的路径,当然要使用这些模板文件,还需要在 alertmanager.yaml 配置文件中指定:

  1. templates:
  2. - '*.tmpl'

创建成功后,Secret 对象将会挂载到 AlertManager 对象创建的 AlertManager Pod 中去。


点击查看本文视频

扫描下面的二维码(或微信搜索k8s技术圈)关注我们的微信公众帐号,在微信公众帐号中回复 加群 即可加入到我们的 kubernetes 讨论群里面共同学习。

k8s技术圈二维码